/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.da.dext.genxml;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import uk.da.dext.datastructs.CategoryStruct;
import uk.da.dext.utilities.Constants;

public class CategoryManagement implements ElementsManagement{
	
	private static final String FIELDS_SEPARATOR = ";";
	/**
	 * Fedora generated PID for the document referenced by this category set, just used once when creating the partial map
	 */
	private String currentDocumentPID;
	/**
	 * 
	 */
	private String currentDocumentXmlId;
	/**
	 * 
	 */
	private List<CategoryStruct> categoriesList;
	
	/**
	 * Categories labels for generating Qudex Instance, pairs value/label
	 */
	private Map<String, String> categoriesMap;
	/**
	 * @param currentDocumentPID the currentDocuementPID to set
	 */
	public void setCurrentDocumentPID(String currentDocumentPID) {
		this.currentDocumentPID = currentDocumentPID;
	}
	/**
	 * @return the currentDocuementPID
	 */
	public String getCurrentDocumentPID() {
		return currentDocumentPID;
	}
	/**
	 * @param currentDocumentXmlId the currentDocumentXmlId to set
	 */
	public void setCurrentDocumentXmlId(String currentDocumentXmlId) {
		this.currentDocumentXmlId = currentDocumentXmlId;
	}
	/**
	 * @return the currentDocumentXmlId
	 */
	public String getCurrentDocumentXmlId() {
		return currentDocumentXmlId;
	}
	/**
	 * Sets the array of categories
	 * @param categoriesList
	 */
	public void setCategoriesList(List<CategoryStruct> categoriesList) {
		this.categoriesList = categoriesList;
	}
	/**
	 * Gets the array of categories
	 * @return categoriesList
	 */
	public List<CategoryStruct> getCategoriesList() {
		return categoriesList;
	}
	
	/**
	 * Constructor
	 */
	public CategoryManagement() {
		this.categoriesList = new ArrayList<CategoryStruct>();
		this.currentDocumentPID = "";
		this.currentDocumentXmlId = "";
		this.categoriesMap = new LinkedHashMap<String, String>();
	}
	/**
	 * Adds a new category to the list of categories, if the category exists then
	 * just the lists of parents and children are updated
	 * @param name name of the category to add a.k.a label
	 * @param parent label of the parent of the category to add
	 * @param child label of the child of the category to add
	 */
	public void addCategory(String name, String parent, String child) {
		CategoryStruct auxCategory = (CategoryStruct) this.findElementByLabel(name);
		
		if (auxCategory == null) {
			auxCategory = new CategoryStruct(name);
			this.categoriesList.add(auxCategory);
		}
		if (parent != "") {
			auxCategory.addCategoryParent(parent);
		}
		if (child != "") {
			auxCategory.addCategoryChild(child);
		}
	}
	/**
	 * Finds a particular category in the array of categories by label value
	 * @param name display label of category to find
	 * @return category object if found, null otherwise
	 */
	public Object findElementByLabel(String name) {
		for (CategoryStruct cat : this.categoriesList) {
			if (cat.getCategoryLabel().equalsIgnoreCase(name)) {
				return cat;
			}
		}
		//New category to add
		return null;
	}
	/**
	 * Finds a particular category in the array of categories by display-label value
	 * @param name display label of category to find
	 * @return category object if found, null otherwise
	 */
	public Object findElementByDisplayLabel(String name) {
		for (CategoryStruct cat : this.categoriesList) {
			if (cat.getCategoryDisplayLabel().equalsIgnoreCase(name)) {
				return cat;
			}
		}
		//New category to add
		return null;
	}
	/**
	 * Clears the list of categories
	 */
	public void clearList() {
		for (CategoryStruct cat : this.categoriesList) {
			cat.clearCategoryLists();
		}
		this.categoriesList.clear();
		this.categoriesMap.clear();
	}
	/**
	 * 
	 * @param categoriesSpreadsheet list of categories extracted from the QuDEx spreadsheet
	 */
	public void constructListOfElements(String language) {
		for (String cat : this.categoriesMap.keySet()) {
			processCategoryHierarchy(cat, this.categoriesMap.get(cat).toString(), language);
		}
		for (CategoryStruct cat : this.categoriesList) {
			addChildrenRelationshipToCategories(cat);
		}		
		// Clear the map for next iteration
		this.categoriesMap.clear();
	}
	/**
	 * 
	 * @param categoryLabel the value of the category, valueCategory field from ss
	 * @param categoryDisplayLabel the display label of the category, labelCategory field from ss
	 * @param language
	 * @return
	 */
	private void processCategoryHierarchy(String categoryLabel, String categoryDisplayLabel, String language) {		
		boolean isNewCategory = false;
		String label;
		//Process display-label and add immediate parent
		String[] catHierarchy = categoryLabel.split(Constants.HIERARCHY_CAT_SEPARATOR);
		String[] catHierarchyDisplay = categoryDisplayLabel.split(Constants.HIERARCHY_CAT_SEPARATOR);
		if (catHierarchy.length == 1) 
		    //Parent category, ex 'category1', split returns array[1] with 'category1' value
		    label = catHierarchy[0];
		else label = catHierarchy[catHierarchy.length - 1];
		CategoryStruct auxCategory = (CategoryStruct) this.findElementByLabel(label);
		
		if (auxCategory == null) {
			auxCategory = new CategoryStruct();
			isNewCategory = true;			
			auxCategory.setCategoryLabel(label);
			//AMG new changes: category displayLabel reflect the category and its parent: cat_parent:cat
			if (catHierarchyDisplay.length > 1) {
				label = catHierarchyDisplay[catHierarchyDisplay.length - 2] + ':' + catHierarchyDisplay[catHierarchyDisplay.length - 1]; 
			}
			else {
				label = catHierarchyDisplay[0];
			}
			auxCategory.setCategoryDisplayLabel(label);
			auxCategory.setCategoryXmlId(QudexManagement.generateID());
			auxCategory.setCategoryLanguage(language);
		}
		//Add current documentRef Fedora PID
		auxCategory.getDocumentRefs().add(this.currentDocumentPID);
		//Add generated XMLID for the Fedora PID
		auxCategory.getDocumentRefsXmlIds().add(this.currentDocumentXmlId);
		if (catHierarchy.length > 1) {
		    //Immediate parent
		    auxCategory.addCategoryParent(catHierarchy[catHierarchy.length - 2]);
		}
		
		//New category not present
		if (isNewCategory) {
			this.categoriesList.add(auxCategory);
		}
	}
	//Recursive function to generate parent-to-children relationship
	private void addChildrenRelationshipToCategories(CategoryStruct auxCategory) {
		if (auxCategory.getImmParents().size() == 1) {
			CategoryStruct parentCat = (CategoryStruct) this.findElementByLabel(auxCategory.getImmParents().get(0));
			if (!parentCat.getImmChildren().contains(auxCategory.getCategoryXmlId()))
			    parentCat.getImmChildren().add(auxCategory.getCategoryXmlId());
		}
		else {
			for (String catLabel : auxCategory.getImmParents()) {
				addChildrenRelationshipToCategories((CategoryStruct) this.findElementByLabel(catLabel));
			}
		}
	}
	/**
	 * Given a document Fedora Pid returns the generated XML-ID associated with it
	 * @param categoryLabel current category display label
	 * @param documentPid fedora document pid to search for
	 * @return the document xml-id, null if not found
	 */
	public String getDocumentXmlId(String categoryLabel, String documentPid) {
		
		CategoryStruct auxCat = (CategoryStruct) this.findElementByLabel(categoryLabel);
		int i = 0;
		if (auxCat != null) {
			for (String document : auxCat.getDocumentRefs()) {
				if (document.equalsIgnoreCase(documentPid)) {
					return auxCat.getDocumentRefsXmlIds().get(i);
				}
				i++;
			}
			return null;
		} else return null;
	}
	/**
	 * Obtain the Qudex XML ID of a document associated with a particular category
	 * @param documentPid Fedora PID of the document its xml id we are searching for
	 * @return document xml id, null if not found
	 */
	public String searchDocumentIds(String documentPid) {
		for (CategoryStruct cat : this.categoriesList) {
			String documentXmlId = getDocumentXmlId(cat.getCategoryLabel(), documentPid);
			if (documentXmlId != null)
				return documentXmlId;
		}
		return null;
	}
	
	public List<?> getElementsList() {
	    // TODO Auto-generated method stub
	    return this.categoriesList;
	}
	
	public boolean generateElementsMap(String valueCat, String labelCat) {
		String[] arrayCatLabel = valueCat.split(FIELDS_SEPARATOR);
		String[] arrayCatDisplayLabel = labelCat
			.split(FIELDS_SEPARATOR);
		if (arrayCatLabel.length != arrayCatDisplayLabel.length)
		    return false;
		for (int i = 0; i < arrayCatLabel.length; i++) {
		    this.categoriesMap.put(arrayCatLabel[i], arrayCatDisplayLabel[i]);
		}
		return true;

	    }
	public Map<?, ?> getElementsMap() {
	    return this.categoriesMap;
	}
	@SuppressWarnings("unchecked")
	public void setElementsMap(Map<?, ?> map) {
	    this.categoriesMap = (Map<String, String>) map;
	}
	@SuppressWarnings("unchecked")
	public void setElementsList(List<?> ellist) {
		this.setCategoriesList((List<CategoryStruct>) ellist);
	}
}
